﻿using MongoDB.Driver;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.ClientDW.Entities.Alerts;
using Sustainalytics.Events.DataAccessLayer;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Sustainalytics.ClientDW.Synchronization
{
    public class ControversyAlertsSyncStep : ISyncStep
    {
        public SyncStepOrder ExecutionOrder => SyncStepOrder.ControversyAlerts;

        public SyncContext Start(SyncContext syncContext)
        {
            var commandTimeout = SyncHelper.ReadCommandTimeOutFromConfig();

            syncContext.Log($"DataReader CommandTimeout: {commandTimeout}");

            string connectionString;

            if (!syncContext.GAConnectionStrings.TryGetValue("ClientDW", out connectionString))
            {
                throw new ArgumentException("No connection string found for ClientDW!");
            }
            syncContext.Log("ClientDw repository acquired based on connection string " + connectionString);

            if (!syncContext.GAConnectionStrings.TryGetValue("EventsConnectionString", out connectionString))
            {
                throw new ArgumentException("No connection string found for Events!");
            }
            var eventsDataReader = EventsDataReader.GetInstance(syncContext.GAConnectionStrings["EventsConnectionString"], commandTimeout);
            syncContext.Log("IEventsDataReader acquired based on connection string " + connectionString);

            // check for task cancellation
            syncContext.Token.ThrowIfCancellationRequested();

            var sw = Stopwatch.StartNew();

            var builder = Builders<CompanyProfileResearchState>.Filter;
            var filter = builder.Eq(cp => cp.ResearchParentId, null)
                       & builder.Eq(cp => cp.ControversyResearchState.ResearchState, ResearchState.ResearchAvailable);

            var companyIds = MongoFactory.GetCollectionFromDatabase<CompanyProfileResearchState>(syncContext.GAConnectionStrings["ClientDW"])
                    .Find(filter)
                    .Project(cd => cd.Id)
                    .ToList();

            var createdAfter = DateTime.UtcNow.AddMonths(-4);

            var clientAlerts = eventsDataReader.ListClientAlerts(createdAfter);

            syncContext.Log($"Retrieved {clientAlerts.Count} alerts created after { createdAfter.Date.ToString(System.Globalization.CultureInfo.InvariantCulture) }.");

            var changedClientAlerts = clientAlerts
                .Where(a => companyIds.Contains(a.CompanyId))
                .ToList();

            syncContext.Log($"{changedClientAlerts.Count} alerts linked to controversy companies with research available.");

            SaveAlerts(syncContext, changedClientAlerts);

            sw.Stop();
            syncContext.Log($"Alerts synchronization completed successfully. Elapsed time: {sw.Elapsed}");

            return syncContext;
        }

        public void SaveAlerts(SyncContext syncContext, List<Alert> documents)
        {
            if (documents.Count == 0) return;

            var collection = MongoFactory.GetCollectionFromDatabase<Alert>(syncContext.GAConnectionStrings["ClientDW"]);

            var alertsIndexes = collection.Indexes.List().ToList();
            if (alertsIndexes.Count <= 1)
            {
                collection.Indexes.CreateOne(Builders<Alert>.IndexKeys.Ascending(x => x.Created).Ascending(x => x.CompanyId));
            }

            var filter = Builders<Alert>.Filter.Empty;

            var deleteResult = collection.DeleteMany(filter);
            collection.InsertMany(documents);

            var rowsAffected = collection.Count(filter);

            syncContext.Log($"{ deleteResult.DeletedCount } alerts removed.");
            syncContext.Log($"{ rowsAffected } alerts inserted.");

            if (documents.Count != rowsAffected)
                throw new Exception("Insert Alert documents failed!");
        }
    }
}
